/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2019-2022. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * Description:
 * Author: huawei
 * Create: 2019-10-15
 */
#include <linux/mm.h>

#include "devmm_adapt.h"
#include "devmm_proc_info.h"
#include "svm_mem_split.h"

static u32 g_support_memory_split_feature = 0;

void devmm_set_memory_split_feature(u32 flag)
{
    g_support_memory_split_feature = flag;

    return;
}

static bool devmm_is_support_memory_split(u32 vfid, int nid)
{
    if ((g_support_memory_split_feature == 0) || (vfid == 0) || (devmm_is_cdm_node(nid) == false)) {
        return false;
    }
    return true;
}

int devmm_normal_free_mem_size_sub(u32 devid, u32 vfid, int nid, u64 page_num)
{
    u64 mem_size;

    /* Only memory split feature need statistic. */
    if (devmm_is_support_memory_split(vfid, nid) == false) {
        return 0;
    }

    mem_size = page_num * PAGE_SIZE;
    if ((u64)atomic64_read(&devmm_svm->device_info.free_mem_size[devid][nid][vfid]) < mem_size) {
        return -ENOMEM;
    }
    atomic64_sub((long)mem_size, &devmm_svm->device_info.free_mem_size[devid][nid][vfid]);
    return 0;
}

void devmm_normal_free_mem_size_add(u32 devid, u32 vfid, int nid, u64 page_num)
{
    u64 mem_size;

    if (devmm_is_support_memory_split(vfid, nid) == false) {
        return;
    }

    mem_size = page_num * PAGE_SIZE;
    atomic64_add((long)mem_size, &devmm_svm->device_info.free_mem_size[devid][nid][vfid]);
}

int devmm_huge_free_mem_size_sub(u32 devid, u32 vfid, int nid, u64 page_num, u32 hugetlb_alloc_flag)
{
    u64 mem_size;

    if (devmm_is_support_memory_split(vfid, nid) == false) {
        return 0;
    }

    mem_size = page_num * HPAGE_SIZE;
    if (hugetlb_alloc_flag == HUGETLB_ALLOC_NORMAL) {
        if ((u64)atomic64_read(&devmm_svm->device_info.free_mem_hugepage_size[devid][nid][vfid]) < mem_size) {
            return -ENOMEM;
        }
        atomic64_sub((long)mem_size, &devmm_svm->device_info.free_mem_hugepage_size[devid][nid][vfid]);
    } else {
        if ((u64)atomic64_read(&devmm_svm->device_info.free_mem_size[devid][nid][vfid]) < mem_size) {
            return -ENOMEM;
        }
        atomic64_sub((long)mem_size, &devmm_svm->device_info.free_mem_size[devid][nid][vfid]);
    }

    return 0;
}

void devmm_huge_free_mem_size_add(u32 devid, u32 vfid, int nid, u64 page_num, u32 hugetlb_alloc_flag)
{
    u64 mem_size;

    if (devmm_is_support_memory_split(vfid, nid) == false) {
        return;
    }

    mem_size = page_num * HPAGE_SIZE;
    if (hugetlb_alloc_flag == HUGETLB_ALLOC_NORMAL) {
        atomic64_add((long)mem_size, &devmm_svm->device_info.free_mem_hugepage_size[devid][nid][vfid]);
    } else {
        atomic64_add((long)mem_size, &devmm_svm->device_info.free_mem_size[devid][nid][vfid]);
    }
}